Wczytywanie pakietów

Zacznijmy od najważniejszej rzeczy czyli od załadowania pakietów. Ja wiem, że to jest oczywiste, ale zasadniczo bez tego ani rusz. Zwłaszcza jeśli chodzi o RMarkdown jest to kluczowe. Kod, który jest w RMarkdown musi być samowystarczalny. Nie może od niczego zależeć. Pierwsze linijki służą do zainstalowania potrzebnych nam pakietów i tak naprawdę trzeba to zrobić tylko raz w życiu (no dobra więcej razy ale na ten moment nie ma to znaczenia).

packages<- c("reshape2","ggplot2","Hmisc","plyr")
if (length(setdiff(packages, rownames(installed.packages()))) > 0) {
  install.packages(setdiff(packages, rownames(installed.packages())))  
}
library(reshape2)
library(ggplot2)
library(Hmisc)
library(plyr)

Wczytywanie danych i zapisywanie

Druga rzecz, która jest dość istotna to wczytanie danych. Jest cała masa sposobów na robienie tego. Od klikania po wpisywanie ścieżki. Ja osobiście preferuje ten drugi sposób, głównie dlatego, że łatwiej utrzymać wtedy reprodukowalność kodu. Moje dwie ulubione funkcje to read.csv() i spss.get(). Poniżej kod, który służy do wczytania danych z SPSSa i pliku csv. Tak jak widać pierwsze argumenty są bliźniaczo podobne - jest to po prostu nazwa pliku. Pozostałe mówią same za siebie. Pozornie z plikami csv nie ma problemu ale czasem może się zdarzyć, że nie chcemy żeby pierwszy wiersz był traktowany jako nazwy kolumn i wtedy po prostu trzeba zmienić ustawienie defultowe funkcji, czasem też kolejne wartości są od siebie oddzielone nie średnikami, a przecinkami - też trzeba to podać jako argument. Żeby to zrobić to po prostu trzeba wpisać w konsole ?read.csv i przeczytać w dokumentacji.

table_mean <- read.csv("table_mean.csv")
badanie_1 <- spss.get("obrazki - gra1.sav",use.value.labels = T, to.data.frame = T)
obrazki - gra1.sav: Very long string record(s) found (record type 7, subtype 14), each will be imported in consecutive separate variables

Zapisywanie danych w pliku jest banalnie proste. Ja najczęściej zapisuje dane w csv za pomocą funkcji write.csv(). Tak samo jak w przypadku read.csv() może mieć wiele argumentów ale najważniejsze są dwa pierwsze czyli nazwa obiektu do zapisania oraz nazwa pliku do którego ma zostać zapisany.

Jak już jesteśmy przy argumentach funkcji to ważną rzeczą jest to, że przy argumentach logicznych może pisać TRUE i FALSE albo T i F. Nie ma to znaczenia, ale jak radzi John Fox (<3) lepiej pisać pełne ‘TRUE’ i FALSE bo o ile można przypisać wartość T i F to ‘TRUE’ i FALSE się nie da. Może się potem okazać, że coś nam przez to nie działa.

Oglądanie danych

Jak już mamy wczytane dane, to dobrze byłoby je zobaczyć. Znowu jest wiele metod, żeby to zrobić. Szymon pokazał fajną nakładkę na data.frame, dlatego ja tylko zasygnalizuje, że można skorzystać ogólnie podzielić metody na takie, które wyświetlą wynik w konsoli i takie, które otworzą oddzielne okno. Funkcja View() wyświetli wynik w oddzielnym oknie, natomiast print() czy po prostu otoczenie nazwy w nawiasy wyświetli wynik w konsoli. Poza tym dwie przydatne funkcje to head() i tail(), pokazują one w konsoli odpowiednio ileś tam pierwszych i ostatnich wierszy. Poniżej przykłady.

# z jakiś przyczyn View nie działa. Nagle przestało działać, więc nie miałem czasu rozwiązać tego problemu
#View(badanie_1)
View(table_mean)
(badanie_1)
print(badanie_1)
head(badanie_1,10)
tail(badanie_1,10)

Przetwarzanie danych z Ariadny

W dalszej części tego pliku jest pokazane jak ja radzę sobie z danymi z Ariadny. Konkretnie z danymi z badania ze zdjęciami. Na pewno jest milion różnych sposobów na poradzenie sobie z takimi danymi i w cale nie uważam, że mój jest najlepszy ale po prostu działa. Z resztą po rozmowie z Szymonem już wiem, że On by różne rzeczy w tym kodzie zrobił zupełnie inaczej.

Klikacze i nudziarze

Dla mnie dość ważną rzeczą jest zrozumienie z jakimi danymi mam doczynienia. Podejrzewam, że każdy tak robi. Ja ogólnie lubię sobie wszytko zobaczyć bo inaczej jest mi trudno sobie wyobrazić. Poniżej są dwa wykresy, jeden to histogram czasu wypełniania, a drugi to boxplot tego samego czasu. Oczywiście jest wiele różnych sposobów na robienie wykresów ale mnie ostatnio John Fox (<3) przekonał do pakietu graphics. Nie wchodząc zbytnio w szczegóły John Fox zawsze ma rację i Jego racja jest lepsza niż innych. Jak (jeśli) będziemy rozmawiać o wizualizacji to wtedy powiemy czym różnią się od siebie różne pakiety graficzne.

hist(badanie_1$czas,main="Histrogram czasu w badaniu 1",xlab="Czas",ylab="Liczebność")

boxplot(badanie_1$czas, main="Boxplot czasu w badaniu 1",ylab="Czas")

Jak wiadomo z czasami na Ariadnie to nie jest wcale tak łatwo, bo jest dużo “klikaczy”, lub też osób, które zostawiają badanie i ono trwa miliard lat. Dlatego warto poza wykresami zobaczyć podstawowe wartości. Te funkcje są dość oczywiste, więc nie ma się w nie co za bardzo zagłębiać.

(range(badanie_1$czas))
[1]  24 832
(mean(badanie_1$czas))
[1] 128.2279
(median(badanie_1$czas))
[1] 109

Usuwanie niepotrzebnych wierszy

Jeśli zaś chodzi o usuwanie niepotrzebnych wierszy czy też filtrowanie obserwacji to jest na to parę różnych sposobów. Jeśli powiedzmy, że chcemy usunąć wszystkie rekordy z czasami poniżej minuty to ja osobiście robię to w sposób następujący (ma to swoje wady i zalety ale o tym za moment).

(badanie_1[badanie_1$czas>60,])

Jak łatwo się domyślić główną wadą tego rozwiązania jest fakt, że jeśli jest więcej warunków to trzeba strasznie uważać, żeby się nie pomylić. Warto więc skontrolować za pomocą funkcji range(), czy też funkcji table() czy mamy wszystkie obserwacje, które chcieliśmy czy może mamy ich za dużo lub za mało. Niestety w R tak już jest, że często funkcja nam zadziała ale wcale nie musi robić tego czego od niej oczekiwaliśmy. Wyobraźmy sobie, że chcemy wziąć czasy powyżej 60 oraz wszystkich mężczyzn.

badanie_1_tylko_M <- badanie_1[((badanie_1$czas>60) + (badanie_1$sex=="Mężczyzna"))>1,]
(table(badanie_1_tylko_M$sex))

  Kobieta Mężczyzna 
        0       439 

Według mnie najrozsądniejszym sposobem jest rozbijanie tego typu wyboru obserwacji na mniejsze części, tzn. najpierw wybrać obserwacje z czasami poniżej 60 i zapisać jako oddzielny plik, potem dopiero podzielić na mężczyzn i kobiety. Z resztą jeśli chodzi o zmienne dychotomiczne to polecam następujący trick.

rows_with_men <- badanie_1$sex=="Mężczyzna"
(table(badanie_1$sex[rows_with_men]))

  Kobieta Mężczyzna 
        0       503 
(table(badanie_1$sex[!rows_with_men]))

  Kobieta Mężczyzna 
      585         0 

No ale przejdźmy dalej z naszym zbiorem danych. Zamieńmy zbiór danych z wszystkimi obserwacjami w taki, w którym będą tylko obserwacje z czasami powyżej 60 sekund. Jak w programach kulinarnych ja już taki zbiór danych mam gotowy.

badanie_1 <- badanie_1[badanie_1$czas>60,]
(range(badanie_1$czas))
[1]  61 832

Poniżej jest funkcja, która jest dość przydatna i którą będę wykorzystywał jeszcze w tym skrypcie colnames(). Zwraca po prostu wektor nazw kolumn. W tym przypadku zmieniam nazwę 14 kolumny na “blok”. Robię to ponieważ to co dziś pokazuje to jest tylko fragment większej analizy i pisałem funkcje uniwersalne do analizowania wielu zbiorów danych. W pozostałych zbiorach danych w kolumnie blok były zapisane warunki, więc tak było po prostu łatwiej.

colnames(badanie_1)[14]<-"blok"

Usuwanie kolumn z brakami danych

Tak jak mówiłem na początku ten zbiór danych ma dość pokrętny sposób zapisywania warunków. Związane jest to głównie ze sposobem w jaki Ariadna wyświetla osobom badanym ankiety. Nie wchodząc w szczegóły zamiast z bazy danych losować tylko te zdjęcia, które trzeba, oni zakrywają te, których nie trzeba. Różnica mała ale straszliwie komplikuje życie. Żeby się z tym uporać ja napisałem sobie następującą funkcję. Jeśli chodzi o pisanie funkcji czy też jakiegokolwiek programów to rozmawiałem kiedyś z Marcinem Możejko, który pokazywał mi jakiś strasznie wydawałoby mi się zawiły kod. Powiedział wtedy, że ogólnie informatycy zaczynają pisanie funkcji od środka i mi to utkwiło w pamięci i tak staram się robić. Ogólnie za pomocą tej funkcji chciałem pozbyć się kolumn wypełnionych samymi brakami danych.

split_and_remove<-function(table){
  #za pomocą funkcji apply przeszukuje zbiór danych w ten sposób, żeby znaleźć kolumny wypełnione samymi brakami danych
  columns_with_na<-apply(table,2,function(x){
    return(sum(is.na(x)))
  })>(dim(table)[1]-1)
  #wybieram tylko te kolumny, które nie mają braków danych
  wynik<-table[,!columns_with_na]
  #szukam kolumn, które mają w nazwie q6. Robię to bo sprawdziłem wcześniej, że tylko kolumny z odpowiedziami mają obok siebie q6
  column_names<-grep("q6",colnames(wynik))
  #zamieniam nazwy kolumn na cyfry od 1 do 9
  colnames(wynik)[column_names]<-c(1:length(column_names))
  return(wynik)
}

No dobra ale ta funkcja nie ma specjalnie zastosowanie do tego zbioru danych bo miałaby tylko zastosowanie dla zbioru danych w którym kolumny byłyby od góry do dołu puste. Dlatego użyję teraz bardzo przydatnej funkcji ddply(), która dzieli zbiór danych na mniejsze i pozwala na przekształcenie tabeli i scalenie jej na powrót. Argumenty tej funkcji są dość oczywiste, zbiór danych, nazwa kolumny według, której ma być podzielona tabela i na sam koniec nazwa funkcji.

badanie_1_dane<-ddply(badanie_1,.(blok),split_and_remove)
(badanie_1_dane)

Zamiana z “wide” na “long”

Jak już usunęliśmy braki danych to przyszedł czas na zamienienie tej bazy danych na long. Tak jak Szymon pokazywał można to zrobić za pomocą pakietu tidyr ale ja osobiście robię to za pomocą funkcji reshape() z pakietu reshape2. Korzystam z tej funkcji bo po prostu jest mi tak wygodnie poza tym ma ona dość duże możliwości. Pierwszy argument to nazwa zbioru danych, drugi to nazwy kolumn, które mają być zamienione, trzeci nazwa zmiennej docelowej, czwarty to kierunek zmiany (w tym wypadku na long).

badanie_1_long<-reshape(badanie_1_dane,varying=as.character(c(1:9)),v.names="gra_zauf",direction="long")
(badanie_1_long)
LS0tCnRpdGxlOiAiU3DDs2pub8WbxIciCmF1dGhvcjogIk1pa29sYWogQmllc2FnYSIKZGF0ZTogIjMgMTAgMjAxNyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQo8c3R5bGU+CmJvZHkgewp0ZXh0LWFsaWduOiBqdXN0aWZ5fQo8L3N0eWxlPgojIyMgV2N6eXR5d2FuaWUgcGFraWV0w7N3CgpaYWN6bmlqbXkgb2QgbmFqd2HFvG5pZWpzemVqIHJ6ZWN6eSBjenlsaSBvZCB6YcWCYWRvd2FuaWEgcGFraWV0w7N3LiBKYSB3aWVtLCDFvGUgdG8gamVzdCBvY3p5d2lzdGUsIGFsZSB6YXNhZG5pY3pvIGJleiB0ZWdvIGFuaSBydXN6LiBad8WCYXN6Y3phIGplxZtsaSBjaG9kemkgbyBSTWFya2Rvd24gamVzdCB0byBrbHVjem93ZS4gS29kLCBrdMOzcnkgamVzdCB3IFJNYXJrZG93biBtdXNpIGJ5xIcgc2Ftb3d5c3RhcmN6YWxueS4gTmllIG1vxbxlIG9kIG5pY3plZ28gemFsZcW8ZcSHLiBQaWVyd3N6ZSBsaW5pamtpIHPFgnXFvMSFIGRvIHphaW5zdGFsb3dhbmlhIHBvdHJ6ZWJueWNoIG5hbSBwYWtpZXTDs3cgaSB0YWsgbmFwcmF3ZMSZIHRyemViYSB0byB6cm9iacSHIHR5bGtvIHJheiB3IMW8eWNpdSAobm8gZG9icmEgd2nEmWNlaiByYXp5IGFsZSBuYSB0ZW4gbW9tZW50IG5pZSBtYSB0byB6bmFjemVuaWEpLgoKYGBge3J9CnBhY2thZ2VzPC0gYygicmVzaGFwZTIiLCJnZ3Bsb3QyIiwiSG1pc2MiLCJwbHlyIikKaWYgKGxlbmd0aChzZXRkaWZmKHBhY2thZ2VzLCByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpKSA+IDApIHsKICBpbnN0YWxsLnBhY2thZ2VzKHNldGRpZmYocGFja2FnZXMsIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSkpICAKfQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoSG1pc2MpCmxpYnJhcnkocGx5cikKYGBgCgojIyMgV2N6eXR5d2FuaWUgZGFueWNoIGkgemFwaXN5d2FuaWUKCkRydWdhIHJ6ZWN6LCBrdMOzcmEgamVzdCBkb8WbxIcgaXN0b3RuYSB0byB3Y3p5dGFuaWUgZGFueWNoLiBKZXN0IGNhxYJhIG1hc2Egc3Bvc29iw7N3IG5hIHJvYmllbmllIHRlZ28uIE9kIGtsaWthbmlhIHBvIHdwaXN5d2FuaWUgxZtjaWXFvGtpLiBKYSBvc29iacWbY2llIHByZWZlcnVqZSB0ZW4gZHJ1Z2kgc3Bvc8OzYiwgZ8WCw7N3bmllIGRsYXRlZ28sIMW8ZSDFgmF0d2llaiB1dHJ6eW1hxIcgd3RlZHkgcmVwcm9kdWtvd2Fsbm/Fm8SHIGtvZHUuIE1vamUgZHdpZSB1bHViaW9uZSBmdW5rY2plIHRvIGByZWFkLmNzdigpYCBpIGBzcHNzLmdldCgpYC4gUG9uacW8ZWoga29kLCBrdMOzcnkgc8WCdcW8eSBkbyB3Y3p5dGFuaWEgZGFueWNoIHogU1BTU2EgaSBwbGlrdSBjc3YuIFRhayBqYWsgd2lkYcSHIHBpZXJ3c3plIGFyZ3VtZW50eSBzxIUgYmxpxbpuaWFjem8gcG9kb2JuZSAtIGplc3QgdG8gcG8gcHJvc3R1IG5hendhIHBsaWt1LiBQb3pvc3RhxYJlIG3Ds3dpxIUgc2FtZSB6YSBzaWViaWUuIFBvem9ybmllIHogcGxpa2FtaSBjc3YgbmllIG1hIHByb2JsZW11IGFsZSBjemFzZW0gbW/FvGUgc2nEmSB6ZGFyennEhywgxbxlIG5pZSBjaGNlbXkgxbxlYnkgcGllcndzenkgd2llcnN6IGJ5xYIgdHJha3Rvd2FueSBqYWtvIG5hend5IGtvbHVtbiBpIHd0ZWR5IHBvIHByb3N0dSB0cnplYmEgem1pZW5pxIcgdXN0YXdpZW5pZSBkZWZ1bHRvd2UgZnVua2NqaSwgY3phc2VtIHRlxbwga29sZWpuZSB3YXJ0b8WbY2kgc8SFIG9kIHNpZWJpZSBvZGR6aWVsb25lIG5pZSDFm3JlZG5pa2FtaSwgYSBwcnplY2lua2FtaSAtIHRlxbwgdHJ6ZWJhIHRvIHBvZGHEhyBqYWtvIGFyZ3VtZW50LiDFu2VieSB0byB6cm9iacSHIHRvIHBvIHByb3N0dSB0cnplYmEgd3Bpc2HEhyB3IGtvbnNvbGUgYD9yZWFkLmNzdmAgaSBwcnplY3p5dGHEhyB3IGRva3VtZW50YWNqaS4KCmBgYHtyfQp0YWJsZV9tZWFuIDwtIHJlYWQuY3N2KCJ0YWJsZV9tZWFuLmNzdiIpCmJhZGFuaWVfMSA8LSBzcHNzLmdldCgib2JyYXpraSAtIGdyYTEuc2F2Iix1c2UudmFsdWUubGFiZWxzID0gVCwgdG8uZGF0YS5mcmFtZSA9IFQpCmBgYAoKWmFwaXN5d2FuaWUgZGFueWNoIHcgcGxpa3UgamVzdCBiYW5hbG5pZSBwcm9zdGUuIEphIG5hamN6xJnFm2NpZWogemFwaXN1amUgZGFuZSB3IGNzdiB6YSBwb21vY8SFIGZ1bmtjamkgYHdyaXRlLmNzdigpYC4gVGFrIHNhbW8gamFrIHcgcHJ6eXBhZGt1IGByZWFkLmNzdigpYCBtb8W8ZSBtaWXEhyB3aWVsZSBhcmd1bWVudMOzdyBhbGUgbmFqd2HFvG5pZWpzemUgc8SFIGR3YSBwaWVyd3N6ZSBjenlsaSBuYXp3YSBvYmlla3R1IGRvIHphcGlzYW5pYSBvcmF6IG5hendhIHBsaWt1IGRvIGt0w7NyZWdvIG1hIHpvc3RhxIcgemFwaXNhbnkuCgpKYWsganXFvCBqZXN0ZcWbbXkgcHJ6eSBhcmd1bWVudGFjaCBmdW5rY2ppIHRvIHdhxbxuxIUgcnplY3rEhSBqZXN0IHRvLCDFvGUgcHJ6eSBhcmd1bWVudGFjaCBsb2dpY3pueWNoIG1vxbxlIHBpc2HEhyBgVFJVRWAgaSBgRkFMU0VgIGFsYm8gYFRgIGkgYEZgLiBOaWUgbWEgdG8gem5hY3plbmlhLCBhbGUgamFrIHJhZHppIEpvaG4gRm94ICg8MykgbGVwaWVqIHBpc2HEhyBwZcWCbmUgJ1RSVUUnIGkgYEZBTFNFYCBibyBvIGlsZSBtb8W8bmEgcHJ6eXBpc2HEhyB3YXJ0b8WbxIcgYFRgIGkgYEZgIHRvICdUUlVFJyBpIGBGQUxTRWAgc2nEmSBuaWUgZGEuIE1vxbxlIHNpxJkgcG90ZW0gb2themHEhywgxbxlIGNvxZsgbmFtIHByemV6IHRvIG5pZSBkemlhxYJhLgoKIyMjIE9nbMSFZGFuaWUgZGFueWNoCgpKYWsganXFvCBtYW15IHdjenl0YW5lIGRhbmUsIHRvIGRvYnJ6ZSBiecWCb2J5IGplIHpvYmFjennEhy4gWm5vd3UgamVzdCB3aWVsZSBtZXRvZCwgxbxlYnkgdG8genJvYmnEhy4gU3p5bW9uIHBva2F6YcWCIGZham7EhSBuYWvFgmFka8SZIG5hIGRhdGEuZnJhbWUsIGRsYXRlZ28gamEgdHlsa28gemFzeWduYWxpenVqZSwgxbxlIG1vxbxuYSBza29yenlzdGHEhyBvZ8OzbG5pZSBwb2R6aWVsacSHIG1ldG9keSBuYSB0YWtpZSwga3TDs3JlIHd5xZt3aWV0bMSFIHd5bmlrIHcga29uc29saSBpIHRha2llLCBrdMOzcmUgb3R3b3J6xIUgb2RkemllbG5lIG9rbm8uIEZ1bmtjamEgYFZpZXcoKWAgd3nFm3dpZXRsaSB3eW5payB3IG9kZHppZWxueW0gb2tuaWUsIG5hdG9taWFzdCBgcHJpbnQoKWAgY3p5IHBvIHByb3N0dSBvdG9jemVuaWUgbmF6d3kgdyBuYXdpYXN5IHd5xZt3aWV0bGkgd3luaWsgdyBrb25zb2xpLiBQb3phIHR5bSBkd2llIHByenlkYXRuZSBmdW5rY2plIHRvIGBoZWFkKClgIGkgYHRhaWwoKWAsIHBva2F6dWrEhSBvbmUgdyBrb25zb2xpIG9kcG93aWVkbmlvIGlsZcWbIHRhbSBwaWVyd3N6eWNoIGkgb3N0YXRuaWNoIHdpZXJzenkuIFBvbmnFvGVqIHByenlrxYJhZHkuCgpgYGB7cn0KIyB6IGpha2nFmyBwcnp5Y3p5biBWaWV3IG5pZSBkemlhxYJhLiBOYWdsZSBwcnplc3RhxYJvIGR6aWHFgmHEhywgd2nEmWMgbmllIG1pYcWCZW0gY3phc3Ugcm96d2nEhXphxIcgdGVnbyBwcm9ibGVtdQojVmlldyhiYWRhbmllXzEpClZpZXcodGFibGVfbWVhbikKKGJhZGFuaWVfMSkKcHJpbnQoYmFkYW5pZV8xKQpoZWFkKGJhZGFuaWVfMSwxMCkKdGFpbChiYWRhbmllXzEsMTApCmBgYAoKIyMjIFByemV0d2FyemFuaWUgZGFueWNoIHogQXJpYWRueQoKVyBkYWxzemVqIGN6xJnFm2NpIHRlZ28gcGxpa3UgamVzdCBwb2themFuZSBqYWsgamEgcmFkesSZIHNvYmllIHogZGFueW1pIHogQXJpYWRueS4gS29ua3JldG5pZSB6IGRhbnltaSB6IGJhZGFuaWEgemUgemRqxJljaWFtaS4gTmEgcGV3bm8gamVzdCBtaWxpb24gcsOzxbxueWNoIHNwb3NvYsOzdyBuYSBwb3JhZHplbmllIHNvYmllIHogdGFraW1pIGRhbnltaSBpIHcgY2FsZSBuaWUgdXdhxbxhbSwgxbxlIG3Ds2ogamVzdCBuYWpsZXBzenkgYWxlIHBvIHByb3N0dSBkemlhxYJhLiBaIHJlc3p0xIUgcG8gcm96bW93aWUgeiBTenltb25lbSBqdcW8IHdpZW0sIMW8ZSBPbiBieSByw7PFvG5lIHJ6ZWN6eSB3IHR5bSBrb2R6aWUgenJvYmnFgiB6dXBlxYJuaWUgaW5hY3plai4KCiMjIyBLbGlrYWN6ZSBpIG51ZHppYXJ6ZQoKRGxhIG1uaWUgZG/Fm8SHIHdhxbxuxIUgcnplY3rEhSBqZXN0IHpyb3p1bWllbmllIHogamFraW1pIGRhbnltaSBtYW0gZG9jenluaWVuaWEuIFBvZGVqcnpld2FtLCDFvGUga2HFvGR5IHRhayByb2JpLiBKYSBvZ8OzbG5pZSBsdWJpxJkgc29iaWUgd3N6eXRrbyB6b2JhY3p5xIcgYm8gaW5hY3plaiBqZXN0IG1pIHRydWRubyBzb2JpZSB3eW9icmF6acSHLiBQb25pxbxlaiBzxIUgZHdhIHd5a3Jlc3ksIGplZGVuIHRvIGhpc3RvZ3JhbSBjemFzdSB3eXBlxYJuaWFuaWEsIGEgZHJ1Z2kgdG8gYm94cGxvdCB0ZWdvIHNhbWVnbyBjemFzdS4gT2N6eXdpxZtjaWUgamVzdCB3aWVsZSByw7PFvG55Y2ggc3Bvc29iw7N3IG5hIHJvYmllbmllIHd5a3Jlc8OzdyBhbGUgbW5pZSBvc3RhdG5pbyBKb2huIEZveCAoPDMpIHByemVrb25hxYIgZG8gcGFraWV0dSBncmFwaGljcy4gTmllIHdjaG9kesSFYyB6Ynl0bmlvIHcgc3pjemVnw7PFgnkgSm9obiBGb3ggemF3c3plIG1hIHJhY2rEmSBpIEplZ28gcmFjamEgamVzdCBsZXBzemEgbmnFvCBpbm55Y2guIEphayAoamXFm2xpKSBixJlkemllbXkgcm96bWF3aWHEhyBvIHdpenVhbGl6YWNqaSB0byB3dGVkeSBwb3dpZW15IGN6eW0gcsOzxbxuacSFIHNpxJkgb2Qgc2llYmllIHLDs8W8bmUgcGFraWV0eSBncmFmaWN6bmUuCgpgYGB7cn0KaGlzdChiYWRhbmllXzEkY3phcyxtYWluPSJIaXN0cm9ncmFtIGN6YXN1IHcgYmFkYW5pdSAxIix4bGFiPSJDemFzIix5bGFiPSJMaWN6ZWJub8WbxIciKQpib3hwbG90KGJhZGFuaWVfMSRjemFzLCBtYWluPSJCb3hwbG90IGN6YXN1IHcgYmFkYW5pdSAxIix5bGFiPSJDemFzIikKYGBgCgpKYWsgd2lhZG9tbyB6IGN6YXNhbWkgbmEgQXJpYWRuaWUgdG8gbmllIGplc3Qgd2NhbGUgdGFrIMWCYXR3bywgYm8gamVzdCBkdcW8byAia2xpa2FjenkiLCBsdWIgdGXFvCBvc8OzYiwga3TDs3JlIHpvc3Rhd2lhasSFIGJhZGFuaWUgaSBvbm8gdHJ3YSBtaWxpYXJkIGxhdC4gRGxhdGVnbyB3YXJ0byBwb3phIHd5a3Jlc2FtaSB6b2JhY3p5xIcgcG9kc3Rhd293ZSB3YXJ0b8WbY2kuIFRlIGZ1bmtjamUgc8SFIGRvxZvEhyBvY3p5d2lzdGUsIHdpxJljIG5pZSBtYSBzacSZIHcgbmllIGNvIHphIGJhcmR6byB6YWfFgsSZYmlhxIcuCgpgYGB7cn0KKHJhbmdlKGJhZGFuaWVfMSRjemFzKSkKKG1lYW4oYmFkYW5pZV8xJGN6YXMpKQoobWVkaWFuKGJhZGFuaWVfMSRjemFzKSkKYGBgCgojIyMgVXN1d2FuaWUgbmllcG90cnplYm55Y2ggd2llcnN6eQoKSmXFm2xpIHphxZsgY2hvZHppIG8gdXN1d2FuaWUgbmllcG90cnplYm55Y2ggd2llcnN6eSBjenkgdGXFvCBmaWx0cm93YW5pZSBvYnNlcndhY2ppIHRvIGplc3QgbmEgdG8gcGFyxJkgcsOzxbxueWNoIHNwb3NvYsOzdy4gSmXFm2xpIHBvd2llZHpteSwgxbxlIGNoY2VteSB1c3VuxIXEhyB3c3p5c3RraWUgcmVrb3JkeSB6IGN6YXNhbWkgcG9uacW8ZWogbWludXR5IHRvIGphIG9zb2JpxZtjaWUgcm9iacSZIHRvIHcgc3Bvc8OzYiBuYXN0xJlwdWrEhWN5IChtYSB0byBzd29qZSB3YWR5IGkgemFsZXR5IGFsZSBvIHR5bSB6YSBtb21lbnQpLgoKYGBge3J9CihiYWRhbmllXzFbYmFkYW5pZV8xJGN6YXM+NjAsXSkKYGBgCgpKYWsgxYJhdHdvIHNpxJkgZG9tecWbbGnEhyBnxYLDs3duxIUgd2FkxIUgdGVnbyByb3p3acSFemFuaWEgamVzdCBmYWt0LCDFvGUgamXFm2xpIGplc3Qgd2nEmWNlaiB3YXJ1bmvDs3cgdG8gdHJ6ZWJhIHN0cmFzem5pZSB1d2HFvGHEhywgxbxlYnkgc2nEmSBuaWUgcG9teWxpxIcuIFdhcnRvIHdpxJljIHNrb250cm9sb3dhxIcgemEgcG9tb2PEhSBmdW5rY2ppIGByYW5nZSgpYCwgY3p5IHRlxbwgZnVua2NqaSBgdGFibGUoKWAgY3p5IG1hbXkgd3N6eXN0a2llIG9ic2Vyd2FjamUsIGt0w7NyZSBjaGNpZWxpxZtteSBjenkgbW/FvGUgbWFteSBpY2ggemEgZHXFvG8gbHViIHphIG1hxYJvLiBOaWVzdGV0eSB3IFIgdGFrIGp1xbwgamVzdCwgxbxlIGN6xJlzdG8gZnVua2NqYSBuYW0gemFkemlhxYJhIGFsZSB3Y2FsZSBuaWUgbXVzaSByb2JpxIcgdGVnbyBjemVnbyBvZCBuaWVqIG9jemVraXdhbGnFm215LiBXeW9icmHFum15IHNvYmllLCDFvGUgY2hjZW15IHd6acSFxIcgY3phc3kgcG93ecW8ZWogNjAgb3JheiB3c3p5c3RraWNoIG3EmcW8Y3p5em4uCgpgYGB7cn0KYmFkYW5pZV8xX3R5bGtvX00gPC0gYmFkYW5pZV8xWygoYmFkYW5pZV8xJGN6YXM+NjApICsgKGJhZGFuaWVfMSRzZXg9PSJNxJnFvGN6eXpuYSIpKT4xLF0KKHRhYmxlKGJhZGFuaWVfMV90eWxrb19NJHNleCkpCgpgYGAKCldlZMWCdWcgbW5pZSBuYWpyb3pzxIVkbmllanN6eW0gc3Bvc29iZW0gamVzdCByb3piaWphbmllIHRlZ28gdHlwdSB3eWJvcnUgb2JzZXJ3YWNqaSBuYSBtbmllanN6ZSBjesSZxZtjaSwgdHpuLiBuYWpwaWVydyB3eWJyYcSHIG9ic2Vyd2FjamUgeiBjemFzYW1pIHBvbmnFvGVqIDYwIGkgemFwaXNhxIcgamFrbyBvZGR6aWVsbnkgcGxpaywgcG90ZW0gZG9waWVybyBwb2R6aWVsacSHIG5hIG3EmcW8Y3p5em4gaSBrb2JpZXR5LiBaIHJlc3p0xIUgamXFm2xpIGNob2R6aSBvIHptaWVubmUgZHljaG90b21pY3puZSB0byBwb2xlY2FtIG5hc3TEmXB1asSFY3kgdHJpY2suCgpgYGB7cn0Kcm93c193aXRoX21lbiA8LSBiYWRhbmllXzEkc2V4PT0iTcSZxbxjenl6bmEiCih0YWJsZShiYWRhbmllXzEkc2V4W3Jvd3Nfd2l0aF9tZW5dKSkKKHRhYmxlKGJhZGFuaWVfMSRzZXhbIXJvd3Nfd2l0aF9tZW5dKSkKCmBgYAoKTm8gYWxlIHByemVqZMW6bXkgZGFsZWogeiBuYXN6eW0gemJpb3JlbSBkYW55Y2guIFphbWllxYRteSB6YmnDs3IgZGFueWNoIHogd3N6eXN0a2ltaSBvYnNlcndhY2phbWkgdyB0YWtpLCB3IGt0w7NyeW0gYsSZZMSFIHR5bGtvIG9ic2Vyd2FjamUgeiBjemFzYW1pIHBvd3nFvGVqIDYwIHNla3VuZC4gSmFrIHcgcHJvZ3JhbWFjaCBrdWxpbmFybnljaCBqYSBqdcW8IHRha2kgemJpw7NyIGRhbnljaCBtYW0gZ290b3d5LgoKYGBge3J9CmJhZGFuaWVfMSA8LSBiYWRhbmllXzFbYmFkYW5pZV8xJGN6YXM+NjAsXQoocmFuZ2UoYmFkYW5pZV8xJGN6YXMpKQpgYGAKClBvbmnFvGVqIGplc3QgZnVua2NqYSwga3TDs3JhIGplc3QgZG/Fm8SHIHByenlkYXRuYSBpIGt0w7NyxIUgYsSZZMSZIHd5a29yenlzdHl3YcWCIGplc3pjemUgdyB0eW0gc2tyeXBjaWUgYGNvbG5hbWVzKClgLiBad3JhY2EgcG8gcHJvc3R1IHdla3RvciBuYXp3IGtvbHVtbi4gVyB0eW0gcHJ6eXBhZGt1IHptaWVuaWFtIG5henfEmSAxNCBrb2x1bW55IG5hICJibG9rIi4gUm9iacSZIHRvIHBvbmlld2HFvCB0byBjbyBkemnFmyBwb2thenVqZSB0byBqZXN0IHR5bGtvIGZyYWdtZW50IHdpxJlrc3plaiBhbmFsaXp5IGkgcGlzYcWCZW0gZnVua2NqZSB1bml3ZXJzYWxuZSBkbyBhbmFsaXpvd2FuaWEgd2llbHUgemJpb3LDs3cgZGFueWNoLiBXIHBvem9zdGHFgnljaCB6YmlvcmFjaCBkYW55Y2ggdyBrb2x1bW5pZSBibG9rIGJ5xYJ5IHphcGlzYW5lIHdhcnVua2ksIHdpxJljIHRhayBiecWCbyBwbyBwcm9zdHUgxYJhdHdpZWouCgpgYGB7cn0KY29sbmFtZXMoYmFkYW5pZV8xKVsxNF08LSJibG9rIgpgYGAKCiMjIyBVc3V3YW5pZSBrb2x1bW4geiBicmFrYW1pIGRhbnljaAoKVGFrIGphayBtw7N3acWCZW0gbmEgcG9jesSFdGt1IHRlbiB6YmnDs3IgZGFueWNoIG1hIGRvxZvEhyBwb2tyxJl0bnkgc3Bvc8OzYiB6YXBpc3l3YW5pYSB3YXJ1bmvDs3cuIFp3acSFemFuZSBqZXN0IHRvIGfFgsOzd25pZSB6ZSBzcG9zb2JlbSB3IGpha2kgQXJpYWRuYSB3ecWbd2lldGxhIG9zb2JvbSBiYWRhbnltIGFua2lldHkuIE5pZSB3Y2hvZHrEhWMgdyBzemN6ZWfDs8WCeSB6YW1pYXN0IHogYmF6eSBkYW55Y2ggbG9zb3dhxIcgdHlsa28gdGUgemRqxJljaWEsIGt0w7NyZSB0cnplYmEsIG9uaSB6YWtyeXdhasSFIHRlLCBrdMOzcnljaCBuaWUgdHJ6ZWJhLiBSw7PFvG5pY2EgbWHFgmEgYWxlIHN0cmFzemxpd2llIGtvbXBsaWt1amUgxbx5Y2llLiDFu2VieSBzacSZIHogdHltIHVwb3JhxIcgamEgbmFwaXNhxYJlbSBzb2JpZSBuYXN0xJlwdWrEhWPEhSBmdW5rY2rEmS4gSmXFm2xpIGNob2R6aSBvIHBpc2FuaWUgZnVua2NqaSBjenkgdGXFvCBqYWtpZWdva29sd2llayBwcm9ncmFtw7N3IHRvIHJvem1hd2lhxYJlbSBraWVkecWbIHogTWFyY2luZW0gTW/FvGVqa28sIGt0w7NyeSBwb2thenl3YcWCIG1pIGpha2nFmyBzdHJhc3puaWUgd3lkYXdhxYJvYnkgbWkgc2nEmSB6YXdpxYJ5IGtvZC4gUG93aWVkemlhxYIgd3RlZHksIMW8ZSBvZ8OzbG5pZSBpbmZvcm1hdHljeSB6YWN6eW5hasSFIHBpc2FuaWUgZnVua2NqaSBvZCDFm3JvZGthIGkgbWkgdG8gdXRrd2nFgm8gdyBwYW1pxJljaSBpIHRhayBzdGFyYW0gc2nEmSByb2JpxIcuIE9nw7NsbmllIHphIHBvbW9jxIUgdGVqIGZ1bmtjamkgY2hjaWHFgmVtIHBvemJ5xIcgc2nEmSBrb2x1bW4gd3lwZcWCbmlvbnljaCBzYW15bWkgYnJha2FtaSBkYW55Y2guIAoKYGBge3J9CnNwbGl0X2FuZF9yZW1vdmU8LWZ1bmN0aW9uKHRhYmxlKXsKICAjemEgcG9tb2PEhSBmdW5rY2ppIGFwcGx5IHByemVzenVrdWplIHpiacOzciBkYW55Y2ggdyB0ZW4gc3Bvc8OzYiwgxbxlYnkgem5hbGXFusSHIGtvbHVtbnkgd3lwZcWCbmlvbmUgc2FteW1pIGJyYWthbWkgZGFueWNoCiAgY29sdW1uc193aXRoX25hPC1hcHBseSh0YWJsZSwyLGZ1bmN0aW9uKHgpewogICAgcmV0dXJuKHN1bShpcy5uYSh4KSkpCiAgfSk+KGRpbSh0YWJsZSlbMV0tMSkKICAjd3liaWVyYW0gdHlsa28gdGUga29sdW1ueSwga3TDs3JlIG5pZSBtYWrEhSBicmFrw7N3IGRhbnljaAogIHd5bmlrPC10YWJsZVssIWNvbHVtbnNfd2l0aF9uYV0KICAjc3p1a2FtIGtvbHVtbiwga3TDs3JlIG1hasSFIHcgbmF6d2llIHE2LiBSb2JpxJkgdG8gYm8gc3ByYXdkemnFgmVtIHdjemXFm25pZWosIMW8ZSB0eWxrbyBrb2x1bW55IHogb2Rwb3dpZWR6aWFtaSBtYWrEhSBvYm9rIHNpZWJpZSBxNgogIGNvbHVtbl9uYW1lczwtZ3JlcCgicTYiLGNvbG5hbWVzKHd5bmlrKSkKICAjemFtaWVuaWFtIG5hend5IGtvbHVtbiBuYSBjeWZyeSBvZCAxIGRvIDkKICBjb2xuYW1lcyh3eW5paylbY29sdW1uX25hbWVzXTwtYygxOmxlbmd0aChjb2x1bW5fbmFtZXMpKQogIHJldHVybih3eW5paykKfQpgYGAKCk5vIGRvYnJhIGFsZSB0YSBmdW5rY2phIG5pZSBtYSBzcGVjamFsbmllIHphc3Rvc293YW5pZSBkbyB0ZWdvIHpiaW9ydSBkYW55Y2ggYm8gbWlhxYJhYnkgdHlsa28gemFzdG9zb3dhbmllIGRsYSB6YmlvcnUgZGFueWNoIHcga3TDs3J5bSBrb2x1bW55IGJ5xYJ5Ynkgb2QgZ8OzcnkgZG8gZG/FgnUgcHVzdGUuIERsYXRlZ28gdcW8eWrEmSB0ZXJheiBiYXJkem8gcHJ6eWRhdG5laiBmdW5rY2ppIGBkZHBseSgpYCwga3TDs3JhIGR6aWVsaSB6YmnDs3IgZGFueWNoIG5hIG1uaWVqc3plIGkgcG96d2FsYSBuYSBwcnpla3N6dGHFgmNlbmllIHRhYmVsaSBpIHNjYWxlbmllIGplaiBuYSBwb3dyw7N0LiBBcmd1bWVudHkgdGVqIGZ1bmtjamkgc8SFIGRvxZvEhyBvY3p5d2lzdGUsIHpiacOzciBkYW55Y2gsIG5hendhIGtvbHVtbnkgd2VkxYJ1Zywga3TDs3JlaiBtYSBiecSHIHBvZHppZWxvbmEgdGFiZWxhIGkgbmEgc2FtIGtvbmllYyBuYXp3YSBmdW5rY2ppLgoKYGBge3J9CmJhZGFuaWVfMV9kYW5lPC1kZHBseShiYWRhbmllXzEsLihibG9rKSxzcGxpdF9hbmRfcmVtb3ZlKQooYmFkYW5pZV8xX2RhbmUpCmBgYAoKIyMjIFphbWlhbmEgeiAid2lkZSIgbmEgImxvbmciCgpKYWsganXFvCB1c3VuxJlsacWbbXkgYnJha2kgZGFueWNoIHRvIHByenlzemVkxYIgY3phcyBuYSB6YW1pZW5pZW5pZSB0ZWogYmF6eSBkYW55Y2ggbmEgbG9uZy4gVGFrIGphayBTenltb24gcG9rYXp5d2HFgiBtb8W8bmEgdG8genJvYmnEhyB6YSBwb21vY8SFIHBha2lldHUgYHRpZHlyYCBhbGUgamEgb3NvYmnFm2NpZSByb2JpxJkgdG8gemEgcG9tb2PEhSBmdW5rY2ppIGByZXNoYXBlKClgIHogcGFraWV0dSBgcmVzaGFwZTJgLiBLb3J6eXN0YW0geiB0ZWogZnVua2NqaSBibyBwbyBwcm9zdHUgamVzdCBtaSB0YWsgd3lnb2RuaWUgcG96YSB0eW0gbWEgb25hIGRvxZvEhyBkdcW8ZSBtb8W8bGl3b8WbY2kuIFBpZXJ3c3p5IGFyZ3VtZW50IHRvIG5hendhIHpiaW9ydSBkYW55Y2gsIGRydWdpIHRvIG5hend5IGtvbHVtbiwga3TDs3JlIG1hasSFIGJ5xIcgemFtaWVuaW9uZSwgdHJ6ZWNpIG5hendhIHptaWVubmVqIGRvY2Vsb3dlaiwgY3p3YXJ0eSB0byBraWVydW5layB6bWlhbnkgKHcgdHltIHd5cGFka3UgbmEgbG9uZykuCgpgYGB7cn0KYmFkYW5pZV8xX2xvbmc8LXJlc2hhcGUoYmFkYW5pZV8xX2RhbmUsdmFyeWluZz1hcy5jaGFyYWN0ZXIoYygxOjkpKSx2Lm5hbWVzPSJncmFfemF1ZiIsZGlyZWN0aW9uPSJsb25nIikKKGJhZGFuaWVfMV9sb25nKQpgYGA=